<?php

/**
 * This file is part of the Pagizer package.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright   Copyright (c) 2010 Advisa (http://www.advisa.fr)
 * @author      Pagizer Core Team <team@pagizer.org>
 * @package     Pagizer
 * @subpackage  backoffice
 */

/**
 * Service to display the left menu (sliding menu)
 */
class m_backoffice_services_MenuService extends f_core_Service
{
	private static $instance;

    private static $LABELWORDWRAP = 15;
	
	/* @var $xmlObject f_xml_Xpath */
	private $xmlObject;
	private $xmlArray;
	private $parserFileName = "treeParser";
	private $module;
    private $nodeId;
	
	private $rootFolderId;
	
	/**
	 * @return m_backoffice_services_MenuService
	 */
	private function __construct()
	{
		return $this;
	}
	
	/**
	 * Get singleton instance
	 *
	 * @return m_backoffice_services_MenuService
	 */
	public static function getInstance()
	{
		if(self::$instance === null)
		{
			self::$instance = new self();
		}
		return self::$instance;
	}
	
// ********************************************* SLIDER MENU ********************************************************
	
	/**
	 * Get the slider menu tree
	 *
	 * @param string $moduleName
	 * @param int $nodeId
	 * @param integer $rootFolderId
	 * @param string $lang
	 * @param bool $ext
	 * @return array
	 */
	public function getMenuTree($moduleName, $nodeId = null, $lang = null, $ext = null, $forceChild = false)
	{
		if(is_null($lang)) $lang = $this->getContextLang();
		if(is_null($ext)) $ext = false;
		if(is_null($moduleName)) $moduleName = "website";
		
		$this->module = $moduleName;

		$this->parseModuleTreefile($moduleName);

		$rootNode = m_backoffice_services_RootNodeService::getInstance()->getModuleRootNode($moduleName, $lang);
		$this->rootFolderId = $rootNode->getUniqueId();	

		if(is_null($nodeId))
		{			
			$nodeId = $this->rootFolderId;
		}

        $this->nodeId = $nodeId;

		$defaultModules = array("website", "medias", "users", "backoffice");
	
		// Special case, if is website rootNode, get all websites
		if($moduleName == "website" && $nodeId == $rootNode->getUniqueId())
		{
			$tree = $this->getAllWebsitesTreeNodes($nodeId, $this->getAuthorizedDocTypes(), $lang);
		}
		elseif($moduleName == "backoffice")
		{
			$tree = $this->getModulesTreeNodes($nodeId, $lang);
		}
		elseif(!in_array($moduleName, $defaultModules) && $forceChild == false)
		{
			$tree = $this->getTreeNodes($nodeId, $this->getAuthorizedDocTypes(), $lang, $moduleName, true);
		}
		else
		{
			$tree = $this->getTreeNodes($nodeId, $this->getAuthorizedDocTypes(), $lang, $moduleName);
		}

		return $tree;
	}
	
	/**
	 * Special case : get nodes for My modules menu
	 *
	 * @param integer $nodeId
	 * @param string $lang
	 * @return array
	 */
	private function getModulesTreeNodes($nodeId, $lang = null)
	{
		$rootNodeService = m_backoffice_services_RootNodeService::getInstance();
		
		$nodes = array();

        // My modules root node
        $nodes[] = $this->newMenuItem($rootNodeService->getModuleRootNode("backoffice", $lang))->setSlide(true)->setAsRoot();

		foreach(f_file_Directory::getInstance(MODULES_DIR)->getSubdirs() as $moduleDir)
		{
            // get module description
			$desc = f_package_Description::getInstance($moduleDir->getPath().DS."package.xml");

			if(!is_null($desc))
			{
                // if module is an extension get menu item
                if($desc->getType() == 'extension')
                {
                    $moduleNode = $desc->hasModuleRootNode() ? $rootNodeService->getModuleRootNode($desc->getName(), $lang) : $rootNodeService->createModuleRootNode($desc->getName(), $lang);
                    $node = $this->newMenuItem($moduleNode)->setId($desc->getName())->setModuleName($desc->getName())->setSlide(true)->setAsChild();
			
                    if(f_context_User::getInstance()->hasRight($desc->getName()."_Show"))
                    {
                        $nodes[$desc->getName()] = $node;
                    }
                }
			}
		}

        $order = f_context_User::getInstance()->getUser()->getExtraParameter('modulesOrder');

        if(!is_null($order))
        {
            $result[0] = $nodes[0];
            unset($nodes[0]);
            
            foreach($order as $k => $module)
            {
                $result[$k+1] = $nodes[$module];
                unset($nodes[$module]);
            }

            $result = array_merge($result, $nodes);
            return $result;
        }

		return $nodes;
	}
	
	private function getTreeNodes($nodeId, $docTypes, $lang = null, $ext = null, $forceChild = false)
	{		
		$lang = strtoupper($lang);

		// if node is a generic module
		if($forceChild == true)
		{
			$rootNodeService = m_backoffice_services_RootNodeService::getInstance();
			$parent = $rootNodeService->getModuleRootNode("backoffice", $lang);

            if(is_null($parent))
            {
                $parent = $rootNodeService->createModuleRootNode("backoffice", $lang);
            }

            $menuItem = $this->newMenuItem($parent)->setSlide(true);

            if($this->rootFolderId == $nodeId) $menuItem->setAsRoot();

            $nodes[] = $menuItem;		
		}		

		$node = $this->getDocumentProvider()->getByUniqueId($nodeId, $lang);

        if($node instanceof m_backoffice_documents_RootNode)
        {
            $node->setModule($this->module);
        }

		$parents = f_relation_ManagerNew::getInstance($node)->getParents()->recursively()->withModel($docTypes)->retrieveIdsAndModels();

		if(is_array($parents))
		{
			$nbParents = count($parents);
			foreach($parents as $key => $parentId)
			{
				$parent = $this->getDocumentProvider()->getByModelAndUniqueId($parentId['model'], $parentId['id'], $lang);

                if($parent instanceof m_backoffice_documents_RootNode)
                {
                    $parent = $parent->getVoDocument()->setModule($this->module);
                }

                $menuItem = $this->newMenuItem($parent)->setSlide($key != ($nbParents-1));

                if($parent instanceof m_backoffice_documents_RootNode)
                {                    
                    $menuItem->setModuleName($this->module);
                }

                if($this->rootFolderId == $nodeId) $menuItem->setAsRoot();
                
				$nodes[] = $menuItem;
			}
		}

		if($node->canSlide() == false || !$node->isTranslated())
		{
			$parentLang = $lang;
			
			if(method_exists($node, "isTranslated"))
			{
				$parentLang = $node->getVO();
			}

			$parents = f_relation_ManagerNew::getInstance($node)->getParents()->withModel($docTypes)->withLang($parentLang)->retrieveDocuments();

            $parent = end($parents);
			$parentNodeId = $parent->getUniqueId();

            foreach($nodes as $k => $node)
            {
                if($node->getUniqueId() == $parentNodeId)
                {
                    $nodes[$k]->setAsLastParent();
                }
            }
            $childs = f_relation_ManagerNew::getInstance($parent)->getChilds()->withModel($docTypes)->retrieveIdsAndModels();
		}
		else
		{
			$childs = f_relation_ManagerNew::getInstance($node)->getChilds()->withModel($docTypes)->retrieveIdsAndModels();
			$nodeItem = array();

			if(isset($nodes) && isset($nodes[count($nodes)-1]))
			{
				$nodes[count($nodes)-1]->setSlide(true);
			}

            $menuItem = $this->newMenuItem($node);

            $menuItem   ->setSlide($node->getUniqueId() == $nodeId && !$ext ? false : true)
                        //->setModuleName($ext)
                        ->setAsTranslated()
                        ->setAsLastParent();

            if($node instanceof m_backoffice_documents_RootNode)
            {
                $menuItem->setModuleName($this->module);
            }

			$nodes[] = $menuItem;
		}

		$childIds = Array();
		$childIdsLang = Array();

		foreach($childs as $childId)
		{
			if($childId["lang"] == $lang)
			{
				$childIds[] = $childId;
			}
			else
			{
				$childIdsLang[] = $childId;
			}
		}
		
		if(!empty($childIds) || !empty($childIdsLang))
		{
			$childIds = array_merge($childIds, $childIdsLang);

			$new = array();
			$exclude = array("");

			for ($i = 0; $i<=count($childIds)-1; $i++)
			{
			    if (!in_array(trim($childIds[$i]["id"]) ,$exclude))
			    {
                    $new[] = $childIds[$i]; $exclude[] = trim($childIds[$i]["id"]);
			    }
			}

			$childIds = $new;
		}

		foreach($childIds as $childId)
		{
            $child = $this->getDocumentProvider()->getByModelAndUniqueId($childId['model'], $childId['id'], $lang);

            if($child->getModel() == 'modules_backoffice/folder')
            {
                if($child->getModule() == $ext)
                {
                    $nodes[] = $this->newMenuItem($child)->setAsChild();
                }
            }
            else
            {
                $nodes[] = $this->newMenuItem($child)->setAsChild();
            }

		}			

		return $nodes;
	}
		
	private function getAllWebsitesTreeNodes($nodeId, $docTypes, $lang)
	{
        $node = f_document_Provider::getInstance()->getByModelAndUniqueId('backoffice/rootNode', $nodeId, $lang);
		$nodes = array();

        // add websites root node
        $nodes[] = $this->newMenuItem($node)->setSlide(false)->setAsLastParent();

        // get childs
		$childs = f_relation_ManagerNew::getInstance($node)->getChilds()->withModel("modules_website/website")->retrieveDocuments();
	
		foreach($childs as $child)
		{
			if(!$child->isATranslation())
			{
                $nodes[] = $this->newMenuItem($child)->setAsChild()->setSlide(true);
			}
		}

		return $nodes;
	}

    private function newMenuItem($document)
    {
        return m_backoffice_extends_MenuNode::createNewFromDocument($document, $this->module, $this->nodeId);
    }
	
// ***********************************************************************************************
	
	/**
	 * Parse the given module menu configuration file
	 *
	 * @param string $moduleName
	 * @param string $xmlFileName
	 * @return m_backoffice_services_MenuService
	 */
	public function parseModuleTreefile($moduleName)
	{		
        $xmlFileName = str_replace(".xml", "", $this->parserFileName).".xml";

		$file = f_file_Resolver::getModuleFilePath($moduleName, 'config'.DS.$xmlFileName);

		if(!is_null($file))
		{
			$this->parseXML($file);
		}
		else
		{
			throw new Exception("No tree parser XML file in ".$filePath);
		}
		
		return $this;
	}
	
	/**
	 * Parse a module menu configuration file
	 *
	 * @param string $xmlFile path to the xml file
	 * @return m_backoffice_services_MenuService
	 */
	private function parseXML($xmlFile)
	{		
		$this->xmlObject = f_xml_Xpath::getInstance($xmlFile);

		$nodes = $this->xmlObject->getElementsFromXpath("items/item");

		$this->xmlArray = array();
		
		if(!is_null($nodes))
		{
			/* @var $node f_xml_XmlElement */
			foreach ($nodes as $node)
			{
				$this->xmlArray[$node->getAttributeValue("documentType")]['attributes'] = $node->getAttributes();

				$actions = $node->getSubElements("actions");
				if(!is_null($actions))
				{
					/* @var $action f_xml_XmlElement */
					foreach($actions as $action)
					{
						$this->xmlArray[$node->getAttributeValue("documentType")]['actions'][$action->getAttributeValue("name")] = $action->getAttributes();
					}
				}
			}
		}
		
		return $this;
	}
	
	/**
	 * Returns authorized document types to display in menu
	 *
	 * @return array|null
	 */
	public function getAuthorizedDocTypes()
	{
		if(is_array($this->xmlArray))
		{
			return array_keys($this->xmlArray);
		}
		return null;
	}
	
	/**
	 * Get actions attributed to a document type
	 *
	 * @param string $documentType
	 * @return array or null
	 */
	public function getActionsByDocumentType($documentType)
	{
		if(isset($this->xmlArray[$documentType]['actions']))
		{
			return $this->xmlArray[$documentType]['actions'];
		}
		return null;
	}
	
	/**
	 * Get attributs for a document type
	 *
	 * @param string $documentType
	 * @return array or null
	 */
	public function getAttributesByDocumentType($documentType)
	{
		if(isset($this->xmlArray[$documentType]['attributes']))
		{
			return $this->xmlArray[$documentType]['attributes'];
		}
		return null;
	}	
}